﻿using UnityEngine;
using System.Collections;
using GeneralStorageClasses;
using ResourceClasses;

namespace TileMapClasses
{
	public delegate void FinishedGeneratingTiles ();

/// <summary>
/// World generation.
/// </summary>
	public class WorldGeneration
	{	
		/// <summary>
		/// The seed.
		/// </summary>
		private float seed;

		/// <summary>
		/// The number of tiles.
		/// </summary>
		private float numberOfTiles;

		/// <summary>
		/// Occurs when <see cref="tileManager"/> has finished loading the sizes.
		/// </summary>
		public static event FinishedGeneratingTiles TileGenStatus; 

		/// <summary>
		/// Initializes a new instance of the <see cref="WorldGeneration"/> class.
		/// </summary>
		/// <param name="seed">Seed.</param>
		public WorldGeneration (float seed)
		{
			//Set the seed
			this.seed = seed;
		}

		/// <summary>
		/// Generates the tiles in <see cref="tileManager"/>.
		/// </summary>
		public void GenTiles ()
		{
			//Get the right amount of tiles
			numberOfTiles = tileManager.Instance.AmountOfTiles;
		
			//Populate the tiles with data according to the seed
			for (int i = 0; i < numberOfTiles; i++) {
				setTileToSeed (i);
			}

			//Generate start locations
			Map mapSize = tileManager.Instance.TileMap;

			//Make start and end tiles along the X-axis
			int startTile = (int)(PerlinSimple (DataLayout.Tile_Start) * 982 % mapSize.Width);
			int endTile = (int)(PerlinSimple (DataLayout.Tile_Enemy_Start) * 833 % mapSize.Width);

			//Place end tile at the back of the map
			endTile = ((int)mapSize.Size - 1) - endTile;

			//Set the controlled of each tile
			tileManager.Instance [startTile].Controlled = 1;
			tileManager.Instance [endTile].Controlled = 2;
			tileManager.Instance.StartTile = startTile;
			tileManager.Instance.EndTile = endTile;

			//Starting population for camp
			tileManager.Instance [startTile].Population = 1000;

			//Remove any enemy tiles around the start tile
			RemoveControlAroundStart (startTile, (int)mapSize.Width);

			if (TileGenStatus != null) {
				TileGenStatus ();
			}
		}

		/// <summary>
		/// Removes the control around starting position.
		/// </summary>
		/// <param name="startPos">Start position.</param>
		/// <param name="width">Width of the map.</param>
		private void RemoveControlAroundStart (int startPos, int width)
		{
			tileManager.Instance [startPos + width].Controlled = 0;
			if (startPos > 0) {
				tileManager.Instance [startPos - 1].Controlled = 0;
				tileManager.Instance [startPos + width - 1].Controlled = 0;
			}
			if (startPos != width - 1) {
				tileManager.Instance [startPos + 1].Controlled = 0;
			}
		}

		/// <summary>
		/// Sets the defaults of a <see cref="Tile"/> to seed.
		/// </summary>
		/// <param name="tileID">Tile ID.</param>
		public void setTileToSeed (int tileID)
		{
			//Get a seeded random between 0 .. 1 multiply it by a number to make it to ints 

			//Generate terrain type 0 - 9 types
			int terrainType = (int)(PerlinGen (DataLayout.Tile_TerrainType, tileID) * 1000 % 2);
			tileManager.Instance [tileID].TerrainType = terrainType;

			//Generate resource on a tile
			tileManager.Instance [tileID].resAcc = GenerateResource (tileID);

			//Use the generated seed data mod the amount of stories for placement
			int storyID = (int)(PerlinGen (DataLayout.Tile_Story, tileID) * 1000 % StoryManager.Instance.Size);
			tileManager.Instance [tileID].storyObj = StoryManager.Instance [storyID];

			//Generate who owns the tile
			int control = (int)(PerlinGen (DataLayout.Tile_Controlled, tileID) * 1000 % 2);
			if (control == 1) {
				control++;
			}
			tileManager.Instance [tileID].Controlled = control;

			Debug.Log (tileManager.Instance [tileID].ToString ());
		}

		/// <summary>
		/// Generates the <see cref="Resource"/> for the given ID.
		/// </summary>
		/// <returns>The <see cref="Resource"/>.</returns>
		/// <param name="tileID">Tile ID.</param>
		private Resource GenerateResource (int tileID)
		{
			//Generate Name (then mod via resources when returning) 
			float typeValue = PerlinGen (DataLayout.Tile_ResourceName, tileID) * 100;

			//Generate amount of resources between 1 - 5
			float amountValue = PerlinGen (DataLayout.Tile_ResourceAmount, tileID) * 1000 % 5 + 1;

			return new Resource (PickResourceName ((int)typeValue), (int)amountValue);
		}

		/// <summary>
		/// Perlin noise for the tile map
		/// </summary>
		/// <returns>A float given the position.</returns>
		/// <param name="x">The x coordinate.</param>
		/// <param name="y">The y coordinate.</param>
		private float PerlinGen (float x, float y)
		{
			return Mathf.PerlinNoise (x, Mathf.PerlinNoise (y / numberOfTiles, seed));
		}

		/// <summary>
		/// Double perlin noise without tile map size.
		/// </summary>
		/// <returns>The simple.</returns>
		/// <param name="x">The x coordinate.</param>
		private float PerlinSimple (float x)
		{
			return Mathf.PerlinNoise (x, Mathf.PerlinNoise (x, seed));
		}

		/// <summary>
		/// Picks the name of the resource.
		/// </summary>
		/// <returns>The <see cref="Resource"/> name.</returns>
		/// <param name="perlinValue">Perlin value for that tile.</param>
		private string PickResourceName (int perlinValue)
		{
			//Mod the value into 0 - 4 use the result
			if (perlinValue % 5 == 0) {
				return "People";
			} else if (perlinValue % 5 == 1) {
				return "Food";
			} else if (perlinValue % 5 == 2) {
				return "Ore";
			} else if (perlinValue % 5 == 3) {
				return "Stone";
			} else if (perlinValue % 5 == 4) {
				return "Lumber";
			}
			//Shouldn't get here ;D
			return "";
		}

		/// <summary>
		/// Gets or sets the seed.
		/// </summary>
		/// <value>The seed.</value>
		public float Seed {
			get {
				return seed;
			}
			set {
				seed = value;
			}
		}
	}
}
